В наличии имеются исторические данные о международных продажах игр (до 2016 г), оценки пользователей и экспертов, жанры и игровые платформы.
Провести предобработку и исследовательский анализ данных, чтобы выявить закономерности, определяющие успешность игры.
pandas, numpy, scipy, matplotlib, seaborn, plotly
#импортируем необходимые библиотеки для проведения исследования
import pandas as pd
import numpy as np
from scipy import stats as st
import warnings
#импортируем необходимые библиотеки для построени графиков
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import seaborn as sns
#интерактивные графики
import plotly.graph_objs as go
from plotly.offline import iplot
import plotly.figure_factory as ff
import plotly.io as pio
pio.renderers.default='notebook'
#считываем данные с сохранением в 'data'
data = pd.read_csv('games.csv')
#выведем 5 первых, 5 последних и 5 случайных записей
display(data.head(5))
display(data.tail(5))
display(data.sample(5))
data.columns
#В названии столбцов присутствуют буквы верхних и нижних регистров, лишних пробелов нет
data.info()
Всего в таблице 11 столбцов.
У шести столбцов тип данных - float64, у пяти - object.
Общее количество записей: 16715.
В 6 столбцах имеются пропуски в данных: Name, Year_of_Release, Genre, Critic_Score, User_Score, Rating.
#приведем к нижнему регистру
data.columns = data.columns.str.lower()
data.head()
#вычислим количество пропусков в столбцах
print(data.isnull().sum())
Разберемся со столбцами где мало пропусков.
1. Рассмотрим столбцы name и genre
data[data['name'].isnull() & data['genre'].isnull()]
#пропуски присутствуют одновременно в 5 столбцах, поэтому лучше удалить их полностью
data.dropna(subset = ['name', 'genre'], inplace = True)
#data=data.fillna({'name': 'unknow', 'genre':'unknow'})
print(data.isnull().sum())
2. Рассмотрим столбец year_of_release
Возможно, что игры выходят на нескольких платформах одновременно и тогда можно восстановить год релиза по другим платформам.
#Запишем названия игр с пропусков годе релиза в список "Name"
Name = data[data['year_of_release'].isnull()]['name']
#Удалим дублирующие элементы
Name=Name.drop_duplicates()
display(len(Name))
#проверим есть ли игры с такими же названиями на других платформах
data_year = data.query('name in @Name')
len(data_year)
#из этого списка нужны игры. которые вышли минимум на двух платформах
name_more_two = data_year.pivot_table(index='name', values='platform', aggfunc = 'count').query('platform > 1')
len(name_more_two)
#~47% пропусков имели релизы на других платформах, найдем эти игры и посчитаем для них медианный год выхода
data_year_more_two = data.query('name in @name_more_two.index').\
pivot_table(index='name', values = 'year_of_release', aggfunc= 'median')
#Осталось удалить строки с пропусками,
#т.к. они говорят о том, что для всех платформ, на которых выходила игра, отсутствует запись о годе релиза.
data_year_more_two = data_year_more_two.dropna()
print(len(data_year_more_two))
data_year_more_two['year_of_release'].value_counts()
#в колонках с годами присутствуют дробных значения, от них мы избавимся в дальнейшем, когда будем приводить к целому типу
#так же отметим, что таких элементов мало, всего 3 игры
data_year_more_two['year_of_release'][15:30]
#сразу отметим, для спортивных игр (типа "FIFA Soccer 2004") релиз происходи на год раньше указанной даты в названии (2003)
#проведем замену пустых значений по полученному словарю
#для проверки
display(data.query('name == "LEGO Harry Potter: Years 5-7"'))
display(data.query('name == "FIFA Soccer 2004"'))
display(data.query('name == "Hitman 2: Silent Assassin"'))
Заметим, что одинаковым названиям игр, соответствуют одинаковые рейтинги ESRB.
for row in data_year_more_two.index:
data.loc[((data['name'] == row) & (data['year_of_release'].isnull())), 'year_of_release'] =\
data_year_more_two['year_of_release'][row]
#print(data_year_more_two['year_of_release'][row])
display(data.query('name == "LEGO Harry Potter: Years 5-7"'))
display(data.query('name == "FIFA Soccer 2004"'))
display(data.query('name == "Hitman 2: Silent Assassin"'))
data.isnull().sum()/len(data)*100
#Остальные года трудно восстановить на основе имеющихся данных. Если сильно надо, можно посмотреть даты релизов в интернете
#по каждой игре. Однако, процент пропусков незначительный меньше одного процента, поэтому удалим данные с пропусками.
data.dropna(subset = ['year_of_release'], inplace = True)
data.isnull().sum()/len(data)*100
3. Рассмотрим столбец rating
т.к. ESRB была основана в 1994 году, проверим оценивала ли данная организация игры, выпущенные годами ранее
data.query('rating == rating and year_of_release < 1994')
ax = data.query('rating != rating')['year_of_release'].plot(kind='hist', grid=True, \
title='Год релиза игры, с отсутсвующим рейтингом')
ax.set_xlabel('year')
plt.show()
Получается, что организация проверяет игры, которые были выпущены до ее существования. И организация выставляет рейтинг не всем играм.
Ранее мы заметили, что одинаковым названиям игр, соответствуют одинаковые рейтинги ESRB. Проверим, что у игры без рейтинга, так же нет рейтинга на другой платформе.
#data.query('rating != rating')
#Запишем названия игр с пропуском рейтинга в список "Name"
Name = data[data['rating'].isnull()]['name']
Name=Name.drop_duplicates()
display(len(Name))
#проверим есть ли игры с такими же названиями на других платформах
name_more_two = data.query('name in @Name').\
pivot_table(index='name', values='platform', aggfunc = 'count').query('platform > 1')
display(len(name_more_two))
display(data.query('name == "Banjo-Kazooie"'))
Получили, что из 5787 игр 939 есть на других платформах
data_rating_more_two = data.query('name in @name_more_two.index and rating == rating').\
pivot_table(index='name', values = 'rating', aggfunc= 'first')
print(len(data_rating_more_two))
#для 359 игр, с пропущенным рейтингом, удается определить рейтинг по другим платформам
#проведем замену
for row in data_rating_more_two.index:
data.loc[((data['name'] == row) & (data['rating'].isnull())), 'rating'] =\
data_rating_more_two['rating'][row]
display(data.query('name == "Banjo-Kazooie"'))
data.isnull().sum()/len(data)*100
Данный подход позволил избавиться от ~2.7% пропусков в столбце. По жанру игр тоже не получается провести адекватную замену, поэтому заменим оставшиеся пропуски на значение - "unknow"
#проведем замену
data.loc[data['rating'].isna(), 'rating'] = 'unknow'
display(data.head())
display(data.isnull().sum())
4. Рассмотрим столбец critic_score
На примере ниже можно убедиться, что для одной и тойже игры у разных платформ различные рейтинги, как у критиков так и пользователей.
#создадим функцию, которая для столбца column строит гистограмму по жанрам
#параметр x_lable - подпись горизонтальной оси
def hist_genre(column, x_lable):
try:
fig, ax = plt.subplots(nrows = 3, ncols=4 , figsize= (15 , 8))
i = 0
for name, group_data in data.groupby('genre'):
ax_cycle = ax[int((i)/4)][(i)%4]
group_data[group_data[column].isnull() == False].plot(kind='hist', y=column, ax=ax_cycle,\
title = name, label='Оценка', alpha=0.65, grid=True)
ax_cycle.set_xlabel(x_lable)
i += 1
fig.tight_layout()
plt.show()
except:
print("ERROR in hist_genre!!!")
display(data.query('name == "LEGO Harry Potter: Years 5-7"'))
#посчитаем коэффициент корреляции
data[data['critic_score'].isna() == False]['critic_score'].\
corr(data[(data['user_score'].isna() == False) &
(data['user_score'] != 'tbd')]['user_score'].astype('float64'))
Присутствует достаточно сильная прямая корреляция между оценками критиков и пользователей (в данных без пропусков), но проводить замену на основе рейтинга пользователей неверно, т.к. случается ситуация, что критики высоко оценивают, а пользователи нет, как и наоборот.
hist_genre('critic_score', 'Оценки критиков')
Проводить замену медианными значениями по жанрам так же будет неправильно, поэтому будем предпологать, что данных просто нет, пропуски можно пометить отрицательным значением: -1. Нулем заменять нельзя, т.к. оценка игры, теоретически, может быть нулевая. Аналогичную замену сделаем в столбце с рейтингом пользователей. Однако, чтобы не вводить сильную путаниц оставим пропуски как есть.
#data=data.fillna({'critic_score': -1, 'user_score': -1})
display(data.head())
data.info()
data.query('user_score == "tbd"').head()
Осталось разобраться с аббревиатурой "tbd" (to be determined) - "подлежит определению". Необходимо, уточнить как происходит сбор данных. Мы предположим, что оценка пользователей начинает отображаться после голосования определенного числа пользователей (условно 100 пользователей), т.е. на данный момент данных нет, но они могут вскоре поступит (например сейчас оценку поставили 80 пользователей). Их также можно пометить отрицательным значением: "-2" и делать фильтрацию, но мы оставим пропуски ка есть.
#data.loc[data['user_score'] == "tbd", 'user_score'] = -2
data.loc[data['user_score'] == "tbd", 'user_score'] = np.NaN
display(data.query('name == "Zumba Fitness"'))
В целом с типами данных в таблицах все неплохо, за исключением столбца с оценками пользователей. Поэтому преобразуем user_score - с типом object к типу float64. И для удобства преобразуем год релиза year_of_release к целому типу int64.
#для проверки
display(data.query('name == "Hitman 2: Silent Assassin"'))
data['user_score'] = data['user_score'].astype('float64')
data['year_of_release'] = data['year_of_release'].astype('int64')
#для проверки
display(data.query('name == "Hitman 2: Silent Assassin"'))
data.info()
Замена типа данных прошла успешно. Отметим, что в year_of_release дробная часть отбросилась в ходе преобразования.
display(data['rating'].value_counts())
В столбце с рейтингом ESRB, есть 4 значения "K-A". Данная аббревиатура является устаревшей для "Е", поэтому произведем соответствующую замену.
data.loc[data['rating'] == 'K-A', 'rating'] = "E"
display(data['rating'].value_counts())
Посчитаем суммарные продажи во всех регионах и запишим их в отдельный столбец all_sales
data['all_sales'] = data['na_sales']+data['eu_sales']+data['jp_sales']+data['other_sales']
data.head()
#найдем количество дублирующих значений в датасете
print(data.duplicated().sum())
В итоге мы обнаружили все пропуски и произвели их замену, отметили встречающиеся аномалии. Где необходимо изменили тип данных. Проверили на наличие дубликатов. Добавили столбец суммарных продаж во всех регионах Преобразованнный DataFrame готов к последующему анализу.
#Для первого случая найдем уникальные игры
data_name = data.pivot_table(index='name', values='year_of_release', aggfunc=['first'])
data_name.columns = ['year']
data_name = data_name['year'].value_counts().sort_index()
#Построим соответствующий график
trace0 = go.Scatter(
x=data.groupby('year_of_release')['year_of_release'].count().index,
y=data.groupby('year_of_release')['year_of_release'].count(),
mode='lines+markers',
name='Игры на всех платформах'
)
trace1 = go.Scatter(
x=data_name.index,
y=data_name,
mode='lines+markers',
name='Уникальные игры'
)
fig = go.Figure(data=[trace0, trace1],\
layout = {'title': 'Количество выпускаемых игр в разные годы', \
'xaxis_title' : 'Годы',\
'yaxis_title' : 'Количество игр'})
fig.show()
После 1993 года наблюдается рост в количестве выпускаемых игры. Пик достигается в 2008 году, затем идет спад (скорее всего связанный с экономически кризисом) и, начиная с 2013г включительно, ежегодно выпускается приблизительно равное количество игр. Также стоит отметить, что производители компьютерных игр начинают практиковать одновременный выход игр на разных платформах с 2002г.
Получается, что для планирования кампании на 2017-й год, наиболее важными оказываются данные с 2013 по 2016гг.
#Вычислим суммарные продажи по платформам.
data_platform_sales = data.pivot_table(index='platform', values='all_sales', aggfunc='sum')
data_platform_sales=data_platform_sales.sort_values(by='all_sales', ascending=False)
#Для выбора платформ с наибольшими суммарными продажами построим круговую диаграмму
labels = data_platform_sales['all_sales'].index
values = data_platform_sales['all_sales']
fig = go.Figure(data=[go.Pie(labels=labels, values=values, hole=0.25)], \
layout = {'title': 'Суммарные продажи игр по платформам за все время'})
fig.show()
ax = data_platform_sales.plot(kind='bar', grid=True, figsize=(15,6))
ax.set_xlabel('Платформа')
ax.set_ylabel('МЛН. $')
ax.set_title("Суммарные продажи игр по платформам за все время")
ax.label='g1'
plt.show()
Для дальнейшего анализа выберем платформы у которых процент доли суммарных продаж выше 8%: PS2, X360, PS3, Wii, DS, PS.
#сохраним названия платформ
name_platform_sales_top = data_platform_sales.head(6).index.values
print(name_platform_sales_top)
#В данных имеется аномалия для платформы DS (сама платформа вышла в 2004 году).
data.query('platform == "DS" and year_of_release < 1994')
#Эту игру 1985 перевыпустили для данной платформы в 2007 году (нашел в интернете), поэтому проведем замену соответсвующую
data.loc[data['name'] == 'Strongest Tokyo University Shogi DS', 'year_of_release'] = 2007
data[data['name'] == 'Strongest Tokyo University Shogi DS']
#Выберем нужный срез, сгрупппируем данные и построем графики
#Для визуализации
trace = []
for i in name_platform_sales_top:
data_name_platform = data.query('platform == @i').\
pivot_table(index='year_of_release', values='all_sales', aggfunc='sum')
data_name_platform.columns = ['sales']
#display(data_name_platform)
trace_i = go.Scatter(
x=data_name_platform.index,
y=data_name_platform['sales'],
mode='lines+markers',
name=i
)
trace.append(trace_i)
fig = go.Figure(data=trace,layout = {'title': 'Суммарные продажи для разных платформ по годам',\
'xaxis_title' : 'Годы',\
'yaxis_title' : 'млн. $'})
fig.show()
Из графика идно, что к 2016 году прекратились продажи для платформ PS, PS2, DS. А у оставшихся к 2016 году сильно упали суммарные продажи. Также из это графика следует, что с 2004 года чаще стали появляться новые платформы. Причем пик суммарных продаж игр на новой платформе наступает через 4-5 лет послее выхода платформы, а затем 6-8 лет суммарные продажи падают. Возможно, что падение продаж на старой платформе связано с выходом игр на новых платформах (пик продаж примерно приходится на годы появления новой платформы).
Приведем расчет среднего значения.
#Всего есть 31 платформа
data['platform'].unique()
platform_life=[]
for name in data['platform'].unique():
data_platform_name = data.query('platform == @name')
first_year = data_platform_name['year_of_release'].sort_values().head(1).values
last_year = data_platform_name['year_of_release'].sort_values().tail(1).values
dist = (last_year - first_year + 1)[0]
platform_life.append(dist)
platform_life_df = pd.DataFrame()
platform_life_df['year_life']= platform_life
platform_life_df['platform'] = data['platform'].unique()
fig = go.Figure(data=go.Bar(
x=platform_life_df['platform'],
y=platform_life_df['year_life'],
), layout = {'title': 'Время существование дяя каждой игровой платформы', 'xaxis_title' : 'Платформы',\
'yaxis_title' : 'Годы'})
fig.show()
platform_life_df.boxplot('year_life', figsize=(6,5))
plt.show()
Такаяя платформа как PC cуществует уже необычно долго, 32 года, что в наших данных является выбросом (boxplot выше это подтверждает). Так же для расчета среднего времени существования платформы нужно исключить недавнопоявившиеся платформы, которые продолжают существовать на 2016 год. Исключать все платформы в 2016, на которых выходят игры, будет неверно, т.к. такие платформы как PS3 и X360 уже прекращают свое существование, это следует из графиков суммарных продаж.
name_new_platform = data.query('year_of_release == 2016')['platform'].unique()
display(platform_life_df.query('platform in @ name_new_platform'))
#Вручную создадим список исключающих платформ,
#здесь будет выброс - PC и молодые платформы, которые существовали меньше 7 лет
name_new_platform = ['PC', 'PS4', 'XOne', 'WiiU', 'PSV']
name_new_platform
#исключим аномальные и молодые платформы из рассмотрения и посчитаем статистические характеристики
platform_life_df.query('platform not in @ name_new_platform').reset_index(drop=True).describe()
Таким образом, среднее значение продолжительности существования платформы составляет 8.5 лет, медианное - 9,5 лет. Выводы, полученные по графикам, были слегка завышены.
#построим ленточную диаграмму (Диаграмму Ганта)
#специальный датафрейм для построения ленточной диаграммы
for_gunt = pd.DataFrame(columns=['Task', "Start", 'Finish'])
#переменная итератор
temp = 0
for i in name_platform_sales_top:
data_name_platform = data.query('platform == @i').\
pivot_table(index='year_of_release', values='all_sales', aggfunc='sum')
#display(data_name_platform.index)
for_gunt.loc[temp] = [i, data_name_platform.index[0], data_name_platform.index[-1]]
temp += 1
fig = ff.create_gantt(for_gunt, show_colorbar=True, bar_width=0.2, showgrid_x=True, showgrid_y=True,\
title='Период продаж игр для разных платформ')
fig.show()
Мы получили, что время от выхода новой платформы до прекращения продаж игр на ней составляет примерно 10-12 лет.
На основе динамики линейки платформ одного производителя PS, PS2, PS3 можно сказать, что новая платформа выход каждые 5-6 лет. Это совпадает с высказаным ранее предположением о том, что пик продаж примерно приходится на дату появления новой платформы.
Расчет среднего времени выхода новой плотформы у PlayStation и Xbox
data_platform_ps = ['PS', 'PS2', 'PS3', 'PS4']
data_platform_xb = ['XB', 'X360', 'XOne']
#'PSV', 'PSP' - это портативные платформы, я их сюда не включил, рассмотрев только стационарные
for_gunt_ps = pd.DataFrame(columns=['Task', "Start", 'Finish'])
for_gunt_xb = pd.DataFrame(columns=['Task', "Start", 'Finish'])
#PS
temp = 0
for i in data_platform_ps:
data_name_platform = data.query('platform == @i').\
pivot_table(index='year_of_release', values='all_sales', aggfunc='sum')
for_gunt_ps.loc[temp] = [i, data_name_platform.index[0], data_name_platform.index[-1]]
temp += 1
for_gunt_ps = for_gunt_ps.sort_values(by='Start')
display(for_gunt_ps)
#Xbox
temp = 0
for i in data_platform_xb:
data_name_platform = data.query('platform == @i').\
pivot_table(index='year_of_release', values='all_sales', aggfunc='sum')
for_gunt_xb.loc[temp] = [i, data_name_platform.index[0], data_name_platform.index[-1]]
temp += 1
for_gunt_xb = for_gunt_xb.sort_values(by='Start')
display(for_gunt_xb)
diff = 0.0
for year in for_gunt_ps['Start']:
if(year == 1994):
first = year;
else:
diff = diff+(year-first)
first = year
print('Cреднее время появления для платформ линейки PS: ', diff/(len(for_gunt_ps)-1))
diff = 0.0
for year in for_gunt_xb['Start']:
if(year == 2000):
first = year;
else:
diff = diff+(year-first)
first = year
print('Cреднее время появления для платформ линейки Xbox: ', diff/(len(for_gunt_xb)-1))
Среднее время выхода новой стационарной консоли у двух крупных производителей PlayStation и Microsoft составляет 6.3 года и 6.5 лет соответственно. Среднее значение продолжительности существования платформы составляет 8.5 лет, медианное - 9,5 лет (рассчитано ранее было)
В результате исследования предыдущих вопросов можно выделить актуальный период: с 2013г по 2016г. По следующим двум причинам:
#возьмем соответствующий срез
#Еще удалим данные по платформе DS потому что после 2013г игры на ней не продавались (см. выше график)
data_relevant = data.query('year_of_release >= 2013 and platform != "DS"').reset_index(drop=True)
print(len(data_relevant))
data_relevant.sample(5)
data_relevant.query('platform == "PSP"')['year_of_release'].value_counts()
На платформе PSP продажи прекратились в 2015году. Поэтому также исключим данную платформу из рассмотрения.
data_relevant = data_relevant.query('platform != "PSP"')
print(len(data_relevant))
data_relevant.sample(5)
#посмотрим какие у нас вообще есть платформы на выбранном отрезке времени
data_relevant['platform'].unique()
#Получаем 9 основных платформ.
display(data_relevant['platform'].value_counts())
#по количеству проданых игр есть лидеры посмотри, что по продажам
#сделаем шрифт крупнее
matplotlib.rcParams.update({'font.size': 12})
fig, ax = plt.subplots(nrows = 3, ncols = 3, figsize=(16, 20))
i=0
for name, group_data in data_relevant.groupby('platform'):
ax_cycle = ax[int((i)/3)][(i)%3]
data_relevant_name_platform = group_data.pivot_table(index='year_of_release', values='all_sales', aggfunc='sum')
data_relevant_name_platform['year'] = data_relevant_name_platform.index
#display(data_relevant_name_platform)
data_relevant_name_platform.plot(x='year', y='all_sales', xlim=(2013, 2016), marker='o', linewidth=2, markersize=12,\
title = name+'\n Общее кол-во проданных игр: '+str(data_relevant['platform'].value_counts()[name]),\
grid = True, label='Суммарные продажи', ax=ax_cycle)
#цена деления основной оси Х
ax_cycle.xaxis.set_major_locator(ticker.MultipleLocator(1))
#цена деления вспомогательной оси Y
ax_cycle.yaxis.set_minor_locator(ticker.MultipleLocator(4))
# Включаем видимость вспомогательных делений:
ax_cycle.minorticks_on()
#задаваем внешний вид вспомогательной сетки
ax_cycle.grid(which='minor', color = 'gray', linestyle = ':')
ax_cycle.set_xlabel('Год')
ax_cycle.set_ylabel('МЛН, $')
i += 1
fig.tight_layout()
plt.show()
Из анализа представленных выше графиков следует, что к 2016 году суммарные продажи упали у всех платформ. Наиболее успешными были PS4 и XOne, т.к. продажи по ним расли с 2013 по 2015 года.
За границу определяющую потенциально прибыльные платформы будем считать суммарные продажи на PC в 2016 году (чуть больше 5 млн.$). Т.е. платформы, у которых суммарныепродажи в 2016г выше 5млн. будем считать потенциально прибыльными, включая платформу PC (т.к. игры для пк никогда не переставали выходить). Таким образом, к потенциально прибыльным платформам можно отнести следующие: PS4, XOne, 3DS и PC.
Построим интерактивный график для удобства анализа и наглядности. Отметим на нем пунктиром среднее и увеличим масштаб по оси OY (рассмотрим значение от 0 до 2.16 млн.$)
trace = []
for name in data_relevant['platform'].unique():
trace.append(
go.Box(
y=data_relevant[data_relevant['platform']==name]['all_sales'],
name=name,
boxmean=True
)
)
# визуализируем данные
fig = go.Figure(data=trace,layout ={'title': '«Ящик с усами» по глобальным продажам каждой игры с разбивкой по платформам',\
'xaxis_title' : 'Платформы',
'yaxis_title' : 'млн. $'
})
#вначале увеличим область
fig.update_yaxes(range=(0.0, 2.02))
fig.show()
#выведем количественные значения медиан и средних
platform_med_mean = data_relevant.pivot_table(index='platform', values='all_sales', aggfunc=['median', 'mean'])
platform_med_mean.columns = ['median', 'mean']
#транспонируем
platform_med_mean = platform_med_mean.T
display(platform_med_mean)
Наличие выбросов на графике скорее всего обусловлены выходом очень популярных у пользователей игр на платформах. Если это так, то удалять их будет неправильно, т.к. компании обычно презентуют дату выхода игры и получается, что наши выбросы не совсем случайны и продавец игр может это использовать.
#Посмотрим выбросы на платформе Xbox one
data_relevant.query('platform == "XOne" and all_sales > 2').sort_values(by="all_sales", ascending=False).head(10)
Действительно все эти игры очень популярны (даже люди далекие от игр скорее всего слышали о каких-то) и о дате релиза компании говорили заранее, поэтому продавец магазина игр может использовать эту информацию для повышения продаж. (как бы получается, что эти выбросы могут быть "прогнозируемыми", в начальный период прадажи резко возрастут, потому что, чтобы разочароваться в игре вначале надо ее купить)
Если рассматривать медианные значения, то все плотформы можно условно разбить на три категории: первая - платформы с медианной суммой продаж меньше 100 тыс.д. (3DS, PC, PSV), данная категория характеризуется малым межквартильным разбросом, вторая - с медианной суммой продаж от 100 до 200 тыс.д.(PS3, Wii), данная категория характеризуется средним межквартильным разбросом и третья - с медианной суммой продаж больше 100 тыс.д. (PS4, WiiU, X360, XOne), эта категория характеризуется большим межквартильным разбросом по сравнению с другими категориями. Минимальная и максимальная разница медианных продаж внутри первой категории составляет 10 и 50 тыс соответственно, внутри второй - 30 тыс. и внутри третьей - 20 и 65 тыс.
Средние значения существенно выше медианных, наши данные скошены влево. С точки зрения продавца игр и получения максимальной выгоды интересны как раз среднее (если следовать нашему предположению, что выбросы это популярные игры о дате релиза которых можно узнать заранее). Поэтому если рассматривать среднее значение, то самой непопулярной платформой будет являться PSV со средними суммарными продажами в 92 тыс. Остальные платформы более успешные у них средние суммарные продажи лежат в диапазоне от 208 тыс.(PC) до 800 тыс.(PS4). И условно платформы можно разбить на 2 категории: первая со средними продажами в диапазоне от 208 до 600 тыс. (PC, 3DS, PS3, Wii, WiiU), вторая - в диапазоне от 650 до 800 тыс. (XOne, X360, PS4).
Если рассматривать медианные продажи, то там была выделена группа суммой продаж меньше 100 тыс.д., куда вошли 3DS и PSV - такие низкие значения можно объяснить тем, что это портативные платформы и стоимость игр для них ниже, чем для стационарных. В эту, первую группу также входи и РС - это вхождение можно попытаться объяснить высоким пиратством для компьютерных игр. Разницу между второй и третьей категорией объясняется тем, что во второй категории находятся устаревающие платформы 7-го поколения, в то время как в третьей категории находятся новые платформы 8-го поколения, кроме X360. Видимо данная платформа находится в последней категории, потому что она стала весьма популярной у пользователей.
Рассмотрм, для примера, платформу PS4
#функция, которая по названию платформы строит диаграммы рассеяния для суммарных продаж от отзывов критиков и пользователей
def user_critic_scatter(name_platform):
try:
data_relevant_for_platform = data_relevant.query('platform == @name_platform').reset_index(drop=True)
print('Всего записей для платформы '+name_platform+': ', len(data_relevant_for_platform))
#сформируем два датафрейма для пользователей и критиков
data_user = data_relevant_for_platform[['user_score', 'all_sales']].reset_index(drop=True)
print('Количество записей об оценках пользователей: ', len(data_user))
data_critic = data_relevant_for_platform[['critic_score', 'all_sales']].reset_index(drop=True)
print('Количество записей об оценках критиков: ', len(data_critic))
#для каждого вычисляем коэффициент корреляции и строим диаграмму рассеяния
fig, ax = plt.subplots(nrows = 1, ncols = 2, figsize=(12,5))
coeff = round(data_user['user_score'].corr(data_user['all_sales']), 2)
data_user.plot(x='user_score', y='all_sales', kind='scatter', ax=ax[0], grid=True,
title='Коэффициент корреляции: '+str(coeff)+'\n')
coeff = round(data_critic['critic_score'].corr(data_critic['all_sales']), 2)
data_critic.plot(x='critic_score', y='all_sales', kind='scatter', ax=ax[1], grid=True,
title='Коэффициент корреляции: '+str(coeff)+'\n')
#data_relevant_name_platform.plot(x='year', y='all_sales', xlim=(2013, 2016), marker='o', linewidth=2, markersize=12,\
# title = name+'\n Общее кол-во проданных игр: '+str(data_relevant['platform'].value_counts()[name]),\
# grid = True, label='Суммарные продажи', ax=ax_cycle)
ax[0].set_xlabel('Оценки пользователей')
ax[0].set_ylabel('МЛН, $')
ax[1].set_ylabel('МЛН, $')
ax[1].set_xlabel('Оценки критиков')
fig.suptitle('Платформа: '+name_platform)
fig.tight_layout(pad=3)
plt.show()
except:
print('ERROR in user_critic_scatter!!!\n')
#user_critic_scatter('PS4')
user_critic_scatter('PS4')
Оценки пользователей практически не влияют на суммарные продажи. Другая ситуация для критиков. Наблюдается прямая корреляция между суммарными продажами и оценками критиков (чем выше оценка, тем выше суммарные продажи). Скорее всего это связано с тем, что критики обычно оценивают игру перед выходом, в то время как пользователе уже после ее покупки и оценки геймплея.
Рассмотрим другие платформы
user_critic_scatter('Wii')
Для данной платформы мало записей, чтобы делать точные выводы.
for name in data_relevant['platform'].unique():
if (name != "PS4") & (name != "Wii"):
user_critic_scatter(name)
Выводы для PS4 справедлив для всех остальных платформ, кроме платформ WiiU и 3DS. Так как для этих двух платформ, помимо того, что наблюдается прямая корреляция между отзывами критиков и суммарными продажами, также наблюдается прямая корреляция между отзывами пользователей и продажами.
3DS и WiiU - это платформы, выпускаемые японской компанией Nintendo. Возможно, что у компании сильно развита политика бетатестирования, где пользователи могут оценить геймплей до официальных продаж и выставить оценку. Тем более, согласно википедии, у WiiU был сложный контроллер и надо было оценить как он понравится людям на разных играх.
data_relevant['genre'].unique()
Игры за рассматриваемый период представлены 12 жанрами. Посмотрим каких жанров игр было больше всего.
data_relevant['genre'].value_counts()
Наиболее популярный выпускаемый жанром игр является - Action, менее популярным - жанр Puzzle. Посмотрим как жанр влияет на суммарные продажи
#Для выбора жанров с высокими и низкими продажами:
#1. построим круговую диаграмму для суммарных продаж по жанрам
#2. построим «Ящик с усами» по глобальным продажам каждой игры с разбивкой по жанрам
from plotly.subplots import make_subplots
fig = make_subplots(rows=1,
cols=2,
column_widths=[0.4, 0.55], #row_heights
specs=[[{"type": "domain"}, {"type": "xy"}]],
subplot_titles=("Суммарные продажи игр",
"«Ящик с усами» по глобальным продажам каждой игры\n"
)
)
#Вычислим суммарные продажи по жанрам.
data_genre_sales = data_relevant.pivot_table(index='genre', values='all_sales', aggfunc='sum')
data_genre_sales=data_genre_sales.sort_values(by='all_sales', ascending=False)
#display(data_genre_sales)
fig.add_trace(go.Pie(labels=data_genre_sales['all_sales'].index, values=data_genre_sales['all_sales'], hole=0.25),
row=1, col=1
)
#построим «Ящик с усами» по глобальным продажам каждой игры с разбивкой по жанрам
for name in data_relevant['genre'].unique():
fig.add_trace(
go.Box(
y=data_relevant[data_relevant['genre']==name]['all_sales'],
name=name,
boxmean=True,
showlegend=False
),
row=1,
col=2
)
fig.update_yaxes(range=(0.0, 3.5), title='млн. $')
fig.update_layout(
title_text="Данные с разбивкой по жанрам за период с 2013 по 2016гг.",
height=420,
width=950,
)
fig.show()
#выведем количественные значения медиан и средних
print('Количественные значения медиан и средних для "ящика с усами"')
genre_med_mean = data_relevant.pivot_table(index='genre', values='all_sales', aggfunc=['median', 'mean'])
genre_med_mean.columns = ['median', 'mean']
#транспонируем
genre_med_mean = genre_med_mean.T
display(genre_med_mean)
С точки зрения суммарных продаж, больше всего дохода приносят Action, Shooter, Sports, Role-Playing. Однако, игр с жанром Action выпускают ~2.5 раза больше, чем другие жанры. Поэтому стоит рассмотреть другой график с boxplot'ами по глобальным продажам игр. Из него следует, что жанром с самыми высокими продажами является - Shooter, на втором и третьем месте после него идут жанры Sports и Platform. Жанрами с низкимим продажами являются - Adventure, Puzzle и Strategy.
data_relevant.head()
data_platform_region = data_relevant.pivot_table(index='platform',
values=['na_sales', 'eu_sales', 'jp_sales'],
aggfunc = 'sum'
)
data_platform_region
#бросается в глаза, что платформа 'PC' в Японии такая не популярная.
#Ради интереса найдем игры вообще за все времена, когда были не нулевые продажи там
data.query('platform == "PC" and jp_sales > 0.0')
#Возвращаемся к заданию
Определим для пользователя каждого региона (NA, EU, JP) топ-5.
data_platform_top_NA = data_platform_region['na_sales'].sort_values(ascending=False).head()
data_platform_top_EU = data_platform_region['eu_sales'].sort_values(ascending=False).head()
data_platform_top_JP = data_platform_region['jp_sales'].sort_values(ascending=False).head()
#Для рассмотрения долей продаж удобно строить круговые диаграмы
fig = make_subplots(rows=1,
cols=3,
column_widths=[0.33, 0.33, 0.34], #row_heights
specs=[[{"type": "domain"}, {"type": "domain"}, {"type": "domain"}]],
subplot_titles=("Северная Америка", "Европа", "Япония")
)
fig.add_trace(go.Pie(labels=data_platform_top_NA.index, values=data_platform_top_NA.values, hole=0.25),
row=1, col=1
)
fig.add_trace(go.Pie(labels=data_platform_top_EU.index, values=data_platform_top_EU.values, hole=0.25),
row=1, col=2
)
fig.add_trace(go.Pie(labels=data_platform_top_JP.index, values=data_platform_top_JP.values, hole=0.25),
row=1, col=3
)
fig.update_layout(
title_text="Доли продаж игр на самых популярных платформах по регионам за период с 2013 по 2016гг.",
height=420,
width=950,
)
fig.show()
Самой популярной платформой в Европе и Северной Америке является PS4, причем в Европе болле популярна. В Японии же самой популярной является 3DS, котроря в двух оставшихся регионах занимает последнее место в топ 5.
По сути весь топ 5 в 3-х регионах поделен в основном между тремя линейками плейстейшен (PS4, PS3, PSV), майкрософт (Xone, X360) и нинтендо (3DS). Если рассматривать с такой точки зрения, то в Северной Америке приблизительно одинаково популярны линейки плейстейшен и майкрософт, в Европе предпочитают больше плейстейшен, а в Японии - линейка игр на платформе плейстейшен и нинтендо, причем здесь в топ-5 вообще нет представителей майкросовт.
Дальше посмотрим как менялись доли продаж по платформам по годам.
У японскго рынка выделяются два отличия:
1) В Японии предпочитают играть не на стационарных платформах, а на портативных.
2) Топ рынка игровых платформ занимают японские производители
#для дальнейшего быстрого построения круговых диаграмм напишем функцию
def pie_part_region(data_na, data_eu, data_jp, title):
try:
fig = make_subplots(rows=1,
cols=3,
column_widths=[0.33, 0.33, 0.34], #row_heights
specs=[[{"type": "domain"}, {"type": "domain"}, {"type": "domain"}]],
subplot_titles=("Северная Америка", "Европа", "Япония")
)
fig.add_trace(go.Pie(labels=data_na.index, values=data_na.values, hole=0.25),
row=1, col=1
)
fig.add_trace(go.Pie(labels=data_eu.index, values=data_eu.values, hole=0.25),
row=1, col=2
)
fig.add_trace(go.Pie(labels=data_jp.index, values=data_jp.values, hole=0.25),
row=1, col=3
)
fig.update_layout(title_text=title, height=400, width=950)
fig.show()
except:
print('ERROR in pie_part_region!!!')
#test
#pie_part_region(data_platform_top_NA, data_platform_top_EU, data_platform_top_JP, 'test')
data_genre_region = data_relevant.pivot_table(index='genre',
values=['na_sales', 'eu_sales', 'jp_sales'],
aggfunc = 'sum'
)
data_genre_top_NA = data_genre_region['na_sales'].sort_values(ascending=False).head()
data_genre_top_EU = data_genre_region['eu_sales'].sort_values(ascending=False).head()
data_genre_top_JP = data_genre_region['jp_sales'].sort_values(ascending=False).head()
#Для рассмотрения долей продаж удобно строить круговые диаграмы
text="Доли продаж игр по самых популярным жанрам по регионам за период с 2013 по 2016гг."
pie_part_region(data_genre_top_NA, data_genre_top_EU, data_genre_top_JP, text)
В Северной Америке и Европе в топ-4 входят одинаковые жанры: 1 место - Action c 33.6% и 36.6% соответствено, 2 место - Shooter c 29.3% и 27.1% соответствено, 3 место - Sports c 17.4% и 18.6% и 4 место - Role-Playing c 12.4% и 11.4%. Жанр на 5-м месте для двух рассматриваемых регионов отличается в Европе предпочитают Racing, а в Северной Америке нестандартные жанры под общим названием Misc.
Иначе делло обстоит в Японии, у них топ-5 следующий: 1-место занимает жанр Role-Playing (44.3%), 2-место - Action (35.1%), 3-место - Misc (8%), 4-место - Fighting (6.76%) и 5 место занимает жанр Shooter (5.85%).
Стоит отметить, что жанр Action занимает приблизительно равную долю во всех регионах (в топ-5): 33.6-36.6%.
#Посмотрим какие рейтинги присутствуют вообще за рассматриваемый период
display(data_relevant['rating'].value_counts())
#исключим из рассмотрения записи с неизвестным рейтингом
data_relevant_rating = data_relevant.query('rating != "unknow"')
print(len(data_relevant))
print(len(data_relevant_rating))
Имеем, что за период с 2013 по 2016гг. у игр, если указано, то было 4 основных рейтинга от ESRB.
#построим столбчатую диаграмму для начала подготовим данные
group_rating=data_relevant_rating.pivot_table(index='rating', values=['na_sales', 'eu_sales', 'jp_sales'], aggfunc='sum')
display(group_rating)
#Для более точного понимания данных составим словарь для рейтинга ESRB, согласно википедии
dict_ESRB ={'E' : 'Для всех (E)',
'E10+' : 'Для всех от 10+ (E10+)',
'M' : 'Для взрослых, 17+ (M)',
'T' : 'Для подростков, 13+ (T)'
}
group_rating=group_rating.set_index(pd.Series(data=dict_ESRB, index=dict_ESRB.keys()))
display(group_rating)
#Для рассмотрения долей продаж построим круговые диаграмы
text="Доли продаж в отдельном регионе в зависимости от рейтинга ESRB с 2013 по 2016гг."
pie_part_region(group_rating['na_sales'], group_rating['eu_sales'], group_rating['jp_sales'], text)
В Европе и Северной Америке наибольший доход ~48% приносят игры с рейтингом M (17+). В Японии наибольший доход 37.8% приносят игры с рейтингом T (13+), который в других двух регионах приносит наименьший доход. Приблизительно четверть доходов в каждом регионе приносят игры с жанром для всех Е.
В целом рынки Северной Америки и Европы похожи по популярным жанрам и платформам, также там одинаковое предпочтение игр по рейтингу ESRB. Особенностью японского рынка игр является, то что пользователи предпочитают портативные платформы стационарным, причем отечественного производства, любимым жанром игр - являются РПГ и Экшен, а наибольший доход приносят
#Подготовим две генеральные совокупности
#data_user_score_pc = data_relevant_user_score.query('platform == "PC"')
data_user_score_pc = data_relevant.query('platform == "PC"')
print(data_user_score_pc['user_score'].describe(),'\n')
#data_user_score_xone = data_relevant_user_score.query('platform == "XOne"')
data_user_score_xone = data_relevant.query('platform == "XOne"')
print(data_user_score_xone['user_score'].describe())
warnings.filterwarnings("ignore")
#посмотрим на их гистограммы
ax = sns.distplot(data_user_score_pc[data_user_score_pc['user_score'].isna() == False]['user_score'],
kde=True, norm_hist=True,
kde_kws={"color": "b", "lw": 2, "label": "PC"});
sns.distplot(data_user_score_xone[data_user_score_xone['user_score'].isna() == False]['user_score'],
kde=True, norm_hist=True,
ax=ax, kde_kws={"color": "r", "lw": 2, "label": "Xbox One"});
#ax = sns.distplot(data_user_score_pc['user_score'], kde=True, norm_hist=True,\
# kde_kws={"color": "b", "lw": 2, "label": "PC"});
#
#sns.distplot(data_user_score_xone['user_score'], kde=True, norm_hist=True, ax=ax,\
# kde_kws={"color": "r", "lw": 2, "label": "Xbox One"});
ax.set_xlabel('Оценки пользователей')
ax.set_title('Распределение оценок пользователей для двух платформ\n', loc='center')
plt.show()
Провериv, что средние пользовательские рейтинги платформ Xbox One и PC одинаковые.
Сформулируем гипотезы
Нулевая гипотеза $H_0$: Средние пользовательские рейтинги платформ «Xbox One» и «PC» равны друг другу;
Альтернативная $H_1$: Средние пользовательские рейтинги платформ «Xbox One» и «PC» не равны друг другуу.
Чтобы проверить гипотезу о равенстве средних двух генеральных совокупностей по взятым из них выборкам, применим метод ttest_ind в качестве параметров метода передадим туда оценки пользователей для двух платформ. Кроме того, мы на гистограмме выше видем, что говорить о равенстве дисперсий трудно, поэтому также передадим методу параметр equal_var со значением False.
Зафиксируем уровень значимости $\alpha$ = 5%
#напишем функцию для проведения исследования
def ttest_rating(df_1, df_2, alpha):
try:
results = st.ttest_ind( df_1, df_2, equal_var = False)
if (results.pvalue < alpha):
print('pvalue= ',results.pvalue,':\t\tpvalue < alpha\t=>\tОтвергаем нулевую гипотезу')
else:
print('pvalue= ',results.pvalue,':\tpvalue > alpha\t=>\tНе получилось отвергнуть нулевую гипотезу')
except:
print("ERROR in ttest_rating")
alpha = 0.05
ttest_rating(data_user_score_xone['user_score'].dropna(), data_user_score_pc['user_score'].dropna(), alpha)
Мы получили, что нулевая гипотеза о равенстве средних пользовательских рейтингов платформ «Xbox One» и «PC» не отвергается при заданом уровне значимости в 5%
#Подготовим две генеральные совокупности
#data_user_score_action = data_relevant_user_score.query('genre == "Action"')
data_user_score_action = data_relevant.query('genre == "Action"')
print(data_user_score_action['user_score'].describe(),'\n')
#data_user_score_sports = data_relevant_user_score.query('genre == "Sports"')
data_user_score_sports = data_relevant.query('genre == "Sports"')
print(data_user_score_sports['user_score'].describe())
#посмотрим на гистограммы
ax = sns.distplot(data_user_score_action[data_user_score_action['user_score'].isna() == False]['user_score'],
kde=True, norm_hist=True,
kde_kws={"color": "b", "lw": 2, "label": "Action"});
sns.distplot(data_user_score_sports[data_user_score_sports['user_score'].isna() == False]['user_score'],
kde=True, norm_hist=True,
ax=ax, kde_kws={"color": "r", "lw": 2, "label": "Sports"});
#ax = sns.distplot(data_user_score_action['user_score'], kde=True, norm_hist=True,\
# kde_kws={"color": "b", "lw": 2, "label": "Action"});
#sns.distplot(data_user_score_sports['user_score'], kde=True, norm_hist=True, ax=ax,\
# kde_kws={"color": "r", "lw": 2, "label": "Sports"});
ax.set_xlabel('Оценки пользователей')
ax.set_title('Распределение оценок пользователей для двух жанров\n', loc='center')
warnings.filterwarnings("ignore")
plt.show()
Провериv, что средние пользовательские рейтинги жанров Action и Sports разные.
Сформулируем гипотезы
Нулевая гипотеза $H_0$: Средние пользовательские рейтинги жанров «Action» и «Sports» равны друг другу;
Альтернативная $H_1$: Средние пользовательские рейтинги жанров «Action» и «Sports» не равны друг другу.
Чтобы проверить гипотезу о равенстве средних двух генеральных совокупностей по взятым из них выборкам, применим метод ttest_ind в качестве параметров метода передадим туда оценки пользователей для двух платформ. Кроме того, мы на гистограмме выше видем, что дисперсии явно различные у двух генеральных совокупностей, поэтому передадим методу параметр equal_var со значением False.
Зафиксируем уровень значимости $\alpha$ = 5%
alpha = 0.05
ttest_rating(data_user_score_action['user_score'].dropna(), data_user_score_sports['user_score'].dropna(), alpha)
Мы получили, что нулевая гипотеза о равенстве средних пользовательских рейтингов жанров «Action» и «Sports» отвергается.
В ходе выполнения проекта был решен ряд задач:
Проведена предобраподка данных. Мы привели названия столбцов к нижнему регистру, заполнили пропуски, привели данные к нужным типам и проверили данные на корректность. Для последующего анализа сделали преобразования над таблицей, добавив столбец с суммарными продажами.
Был проведен исследовательский анализ данных На основе анализа времени существования игровых платформ и подсчета количества игр, выпускаемых ежегодно, был определен актуальный период (с 2013 по 2016 годы) для прогноза на 2017 год. По динамике продаж за актуальный период были выбраны потенциально прибыльные платформы: PS4, XOne, 3DS и PC
Были построены графики по глобальным продажам игр с разбивкой по платформам, который позволил ответить на вопросы о средних и медианных разницах в продажах на разных платформах. Рассмотрев медианные значения продаж, тплотформы были условно разбить на три категории: первая - платформы с медианной суммой продаж меньше 100 тыс.д. (3DS, PC, PSV), данная категория характеризуется малым межквартильным разбросом, вторая - с медианной суммой продаж от 100 до 200 тыс.д.(PS3, Wii), данная категория характеризуется средним межквартильным разбросом и третья - с медианной суммой продаж больше 100 тыс.д. (PS4, WiiU, X360, XOne). Было показано, что наши данные скошены влево: средние значения существенно выше медианных. Поэтому если рассматривать среднее значение, то самой непопулярной платформой будет являться PSV со средними суммарными продажами в 92 тыс. Остальные платформы более успешные у них средние суммарные продажи лежат в диапазоне от 208 тыс.(PC) до 800 тыс.(PS4). И условно платформы можно разбить на 2 категории: первая со средними продажами в диапазоне от 208 до 600 тыс. (PC, 3DS, PS3, Wii, WiiU), вторая - в диапазоне от 650 до 800 тыс. (XOne, X360, PS4).
Исследовано по платформам как отзывы пользователей и критиков на игры влияют на продажи. Для всех рассмотреных платформ, кроме платформ WiiU и 3DS, оценки пользователей практически не влияют на суммарные продажи. Другая ситуация для критиков. Наблюдается прямая корреляция между суммарными продажами и оценками критиков (чем выше оценка, тем выше суммарные продажи). Для платформ WiiU и 3DS кроме того, что наблюдается прямая корреляция между отзывами критиков и суммарными продажами, также наблюдается прямая корреляция между отзывами пользователей и продажами.
Рассмотрены распределения игр по жанрам и выделены жанры с высокимим и низкими продажами. Жанром с самыми высокими продажами является - Shooter, на втором и третьем месте после него идут жанры Sports и Platform. Жанрами с низкимим продажами являются - Adventure, Puzzle и Strategy. Стоит отметить, что игры с жанром Action выпускают ~2.5 раза больше, чем другие жанры. И с точки зрения суммарных продаж, больше всего дохода приносят Action, Shooter, Sports, Role-Playing.
Определены для пользователя каждого региона самые популярные платформы и жанры *Самой популярной платформой в Европе и Северной Америке является PS4, причем в Европе болле популярна. В Японии же самой популярной является 3DS, котроря в двух оставшихся регионах занимает последнее место в топ 5. К 2016 году платформа PS4 нарастила свое влияние во всех трех регионах. В Японии традиционно лидирует 3DS. В 2016 году доля XOne по регионам в топ-5: Северная Америка - 32.9%, Европа - 19.8%, Япония - 0%. Также приобретает популярность PC с 2014 в Европе, а с 2016 появляется в топе на последней позиции и в Северной Америке. В Японии данная платформа отсутствует в топе во всех рассматриваемых периодах.
Было проанализировано влияние рейтинга ESRB на продажи в регионах. Где показано, что в Европе и Северной Америке наибольший доход ~48% приносят игры с рейтингом M (17+). В Японии наибольший доход 37.8% приносят игры с рейтингом T (13+), который в других двух регионах приносит наименьший доход. Приблизительно четверть доходов в каждом регионе приносят игры с жанром для всех - Е.
Были проверены две гипотезы. Первая гипотеза о том, что средние пользовательские рейтинги платформ Xbox One и PC одинаковые. Вторая - cредние пользовательские рейтинги жанров Action и Sports разные. Применив для проверки статистичексий критерий, обе гипотезы были приняты. Выводы также были подтверждены соответствующими гистограммами.